//! @file   lc_iterator.h
//! @brief  
//! @author Sayan Chaliha
//! @date   September, 2011
//!
//! 
#ifndef _LC_ITERATOR_H
#define _LC_ITERATOR_H

#include "libcyan_internal.h"
#include "lc_types.h"

namespace cyan {
  struct CYAN_API InputIteratorTag  {};
  struct CYAN_API OutputIteratorTag {};

  struct CYAN_API ForwardIteratorTag      : public InputIteratorTag         {};
  struct CYAN_API BidirectionalIteratorTag: public ForwardIteratorTag       {};
  struct CYAN_API RandomAccessIteratorTag : public BidirectionalIteratorTag {};

  template <typename C, typename T, typename D = PtrDiff, typename P = T*,
      typename R = T&>
  struct CYAN_API Iterator {
    typedef C IteratorCategory;
    typedef T ValueType;
    typedef D DifferenceType;
    typedef P Pointer;
    typedef R Reference;
  };

  template <typename I>
  struct CYAN_API IteratorTraits {
    typedef typename I::IteratorCategory    IteratorCategory;
    typedef typename I::ValueType           ValueType;
    typedef typename I::DifferenceType      DifferenceType;
    typedef typename I::Pointer             Pointer;
    typedef typename I::Reference           Reference;
  };

  template <typename T>
  struct CYAN_API IteratorTraits<T*> {
    typedef RandomAccessIteratorTag         IteratorCategory;
    typedef T                               ValueType;
    typedef PtrDiff                         DifferenceType;
    typedef T*                              Pointer;
    typedef T&                              Reference;
  };

  template <typename T>
  struct CYAN_API IteratorTraits<const T*> {
    typedef RandomAccessIteratorTag         IteratorCategory;
    typedef T                               ValueType;
    typedef PtrDiff                         DifferenceType;
    typedef const T*                        Pointer;
    typedef const T&                        Reference;
  };

  template <typename I>
  typename IteratorTraits<I>::DifferenceType
  distance_(I first, I last, InputIteratorTag) {
    typename IteratorTraits<I>::DifferenceType n = 0;
    while (first != last) {
      ++first;
      ++n;
    }
    return n;
  }

  template <typename I>
  typename IteratorTraits<I>::DifferenceType
  distance_(I first, I last, RandomAccessIteratorTag) {
    return typename IteratorTraits<I>::DifferenceType(last - first);
  }

  template <typename I>
  typename IteratorTraits<I>::DifferenceType CYAN_API
  distance(I first, I last) {
    return distance_(first, last,
        typename IteratorTraits<I>::IteratorCategory());
  }

  template <typename I, typename J>
  typename IteratorTraits<I>::DifferenceType
  distance_(I first, J last, RandomAccessIteratorTag) {
    return typename IteratorTraits<I>::DifferenceType(last - first);
  }

  template <typename I, typename J>
  typename IteratorTraits<I>::DifferenceType CYAN_API
  distance(I first, J last) {
    return distance_(first, last,
        typename IteratorTraits<I>::IteratorCategory());
  }
}

#endif /* _LC_ITERATOR_H */
